package com.google.code.jtasks.server.dao.mongo;

import com.google.code.jtasks.server.dao.TaskDAO;
import com.google.code.jtasks.common.data.Task;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import com.mongodb.*;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * An implementation of {@link TaskDAO} which uses MongoDB as a backend.
 */
public class MongoTaskDAO implements TaskDAO {
    private static final Logger LOG = LoggerFactory.getLogger(MongoTaskDAO.class);

    private final DB taskDatabase;


    @Inject
    public MongoTaskDAO(DB taskDatabase) {
        this.taskDatabase = taskDatabase;
    }

    @Override
    public String saveTask(Task task) {
        Preconditions.checkNotNull(task, "Task is null");

        LOG.debug("Saving task to database: {}", task);

        DBCollection taskCollection = taskDatabase.getCollection("tasks");

        BasicDBObject object = toDBObject(task);

        taskCollection.save(object);

        String newId = object.get("_id").toString();
        LOG.debug("New task id: {}", newId);
        return newId;
    }

    @Override
    public Optional<Task> loadTask(String id) {
        Preconditions.checkNotNull(id, "Task id is null");

        LOG.debug("Loading task from database: task id {}", id);

        DBCollection taskCollection = taskDatabase.getCollection("tasks");

        DBObject object = taskCollection.findOne(new BasicDBObject("_id", new ObjectId(id)));
        if (object == null) {
            return Optional.absent();
        } else {
            return Optional.of(fromDBObject(object));
        }
    }

    @Override
    public boolean deleteTask(String id) {
        Preconditions.checkNotNull(id, "Task id is null");

        LOG.debug("Deleting task from database: task id {}", id);

        DBCollection taskCollection = taskDatabase.getCollection("tasks");

        WriteResult result = taskCollection.remove(new BasicDBObject("_id", new ObjectId(id)));
        return result.getN() > 0;
    }

    @Override
    public List<Task> loadAllTasks() {
        DBCollection taskCollection = taskDatabase.getCollection("tasks");

        LOG.debug("Loading all tasks from database");

        ImmutableList.Builder<Task> tasks = ImmutableList.builder();
        for (DBObject object : taskCollection.find()) {
            tasks.add(fromDBObject(object));
        }

        return tasks.build();
    }

    public static Task fromDBObject(DBObject dbObject) {
        Preconditions.checkNotNull(dbObject, "DB object is null");

        return new Task(
            Optional.of(dbObject.get("_id").toString()),
            (String) dbObject.get("name"),
            new DateTime(dbObject.get("startTime")),
            new DateTime(dbObject.get("endTime"))
        );
    }

    public static BasicDBObject toDBObject(Task task) {
        Preconditions.checkNotNull(task, "Task is null");

        BasicDBObject dbObject = new BasicDBObject();
        if (task.id.isPresent()) {
            dbObject.append("_id", new ObjectId(task.id.get()));
        }
        return dbObject
            .append("name", task.name)
            .append("startTime", task.startTime.toDate())
            .append("endTime", task.endTime.toDate());
    }
}

